import {
    createConnection,
    TextDocuments,
    ProposedFeatures,
    TextDocumentSyncKind,
    InitializeResult,
    TextDocumentPositionParams,
    Location,
    Connection
} from 'vscode-languageserver/node';
import { TextDocument } from 'vscode-languageserver-textdocument';
import * as fs from 'fs';
import * as path from 'path';
import { Position, Range } from 'vscode-languageserver/node';

// Helper function to escape special characters in regex
function escapeRegExp(string: string): string {
    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

// Helper function to check if a character is a valid identifier character
function isIdentifierChar(char: string): boolean {
    return /[\w$\u4e00-\u9fa5]/.test(char);  // 包含字母、数字、下划线、$和中文字符
}

// Helper function to check if a string is a valid identifier
function isValidIdentifier(word: string): boolean {
    // 标识符必须以字母、下划线或$开头
    if (!/^[a-zA-Z_$\u4e00-\u9fa5]/.test(word)) {
        return false;
    }
    
    // 标识符中间可以包含字母、数字、下划线、$和中文字符
    return /^[\w$\u4e00-\u9fa5]+$/.test(word);
}

// Helper function to get word range at position with improved identifier handling
function getWordRange(doc: TextDocument, position: Position): Range | undefined {
    try {
        const text = doc.getText();
        const lines = text.split('\n');
        
        // 如果位置超出范围，返回undefined
        if (position.line >= lines.length) {
            return undefined;
        }
        
        const line = lines[position.line];
        if (!line || position.character >= line.length) {
            return undefined;
        }

        let start = position.character;
        let end = position.character;
        
        // 向左扩展，包含更多有效的标识符字符
        while (start > 0 && isIdentifierChar(line[start - 1])) {
            start--;
        }
        
        // 向右扩展，包含更多有效的标识符字符
        while (end < line.length && isIdentifierChar(line[end])) {
            end++;
        }
        
        // 如果没有找到有效的单词范围，返回undefined
        if (start === end) {
            return undefined;
        }
        
        // 验证提取的单词是否是有效的标识符
        const word = line.substring(start, end);
        if (!isValidIdentifier(word)) {
            return undefined;
        }
        
        return Range.create(position.line, start, position.line, end);
    } catch (error) {
        connection.console.error(`getWordRange error: ${error}`);
        return undefined;
    }
}

// Helper function to check if position is in a comment
function isInComment(doc: TextDocument, position: Position): boolean {
    try {
        const text = doc.getText();
        const lines = text.split('\n');
        
        if (position.line >= lines.length) {
            return false;
        }
        
        const lineText = lines[position.line];
        
        // 检查单行注释
        const singleLineComment = lineText.indexOf('//');
        if (singleLineComment !== -1 && position.character > singleLineComment) {
            return true;
        }
        
        // 检查多行注释
        let inComment = false;
        let commentStart = -1;
        
        for (let i = 0; i <= position.line; i++) {
            const currentLine = lines[i];
            
            // 查找注释开始和结束标记
            let j = 0;
            while (j < currentLine.length) {
                if (!inComment) {
                    if (currentLine.substr(j, 2) === '/*') {
                        inComment = true;
                        commentStart = j;
                        j += 2;
                    } else {
                        j++;
                    }
                } else {
                    if (currentLine.substr(j, 2) === '*/') {
                        inComment = false;
                        j += 2;
                    } else {
                        j++;
                    }
                }
            }
            
            // 检查当前位置是否在注释中
            if (i === position.line) {
                if (inComment) {
                    return true;
                }
                if (commentStart !== -1 && position.character > commentStart) {
                    const commentEnd = currentLine.indexOf('*/', commentStart);
                    if (commentEnd === -1 || position.character < commentEnd) {
                        return true;
                    }
                }
            }
        }
        
        return false;
    } catch (error) {
        connection.console.error(`isInComment error: ${error}`);
        return false;
    }
}

// Helper function to check if position is in XML comment
function isInXmlComment(doc: TextDocument, position: Position): boolean {
    try {
        const text = doc.getText();
        const lines = text.split('\n');
        
        if (position.line >= lines.length) {
            return false;
        }
        
        const lineText = lines[position.line];
        
        // 检查当前行是否以XML注释开头
        if (lineText.trim().startsWith('<!--')) {
            return true;
        }
        
        // 检查是否在多行XML注释中
        let inComment = false;
        let commentStart = -1;
        
        for (let i = 0; i <= position.line; i++) {
            const currentLine = lines[i];
            
            // 查找注释开始和结束标记
            let j = 0;
            while (j < currentLine.length) {
                if (!inComment) {
                    if (currentLine.substr(j, 4) === '<!--') {
                        inComment = true;
                        commentStart = j;
                        j += 4;
                    } else {
                        j++;
                    }
                } else {
                    if (currentLine.substr(j, 3) === '-->') {
                        inComment = false;
                        j += 3;
                    } else {
                        j++;
                    }
                }
            }
            
            // 检查当前位置是否在注释中
            if (i === position.line) {
                if (inComment) {
                    return true;
                }
                if (commentStart !== -1 && position.character > commentStart) {
                    const commentEnd = currentLine.indexOf('-->', commentStart);
                    if (commentEnd === -1 || position.character < commentEnd) {
                        return true;
                    }
                }
            }
        }
        
        return false;
    } catch (error) {
        connection.console.error(`isInXmlComment error: ${error}`);
        return false;
    }
}

// Create a connection for the server
const connection: Connection = createConnection(ProposedFeatures.all);

// Cache for parsed file contents
const fileCache = new Map<string, { version: number; content: string }>();

// Watch for file changes
connection.onDidChangeWatchedFiles((params) => {
    for (const change of params.changes) {
        const filePath = change.uri.toString().replace('file://', '');
        if (fileCache.has(filePath)) {
            fileCache.delete(filePath);
            connection.console.log(`Cache invalidated for ${filePath}`);
        }
    }
});

// Create a simple text document manager
const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);

// Mapper to store Java <-> XML mappings
const mapperMappings = new Map<string, string>();
const xmlMappings = new Map<string, string>();

connection.onInitialize((params): InitializeResult => {
    return {
        capabilities: {
            textDocumentSync: TextDocumentSyncKind.Incremental,
            // Tell the client that the server supports code lens
            definitionProvider: true
        }
    };
});

// Parse Java Mapper files and store method mappings
connection.onDidOpenTextDocument((params) => {
    const doc = documents.get(params.textDocument.uri);
    if (doc?.languageId === 'java') {
        parseJavaMapper(doc);
    }
});

// Parse XML Mapper files and store statement mappings
connection.onDidOpenTextDocument((params) => {
    const doc = documents.get(params.textDocument.uri);
    if (doc?.languageId === 'xml') {
        parseXmlMapper(doc);
    }
});

// Handle jump to definition requests
connection.onDefinition((params: TextDocumentPositionParams): Location[] => {
    try {
        const doc = documents.get(params.textDocument.uri);
        if (!doc) return [];

        if (doc.languageId === 'java') {
            // Jump from Java to XML
            const methodName = getMethodNameAtPosition(doc, params.position);
            if (methodName && mapperMappings.has(methodName)) {
                const xmlUri = mapperMappings.get(methodName);
                if (xmlUri) {
                    // Try to find exact position in XML
                    const xmlPath = xmlUri.toString().replace('file://', '');
                    if (fs.existsSync(xmlPath)) {
                        const xmlContent = fs.readFileSync(xmlPath, 'utf8');
                        
                        // 支持多种SQL语句类型
                        const statementTypes = ['select', 'insert', 'update', 'delete'];
                        for (const type of statementTypes) {
                            const methodTag = `<${type}[^>]*id=["']${escapeRegExp(methodName)}["']`;
                            const methodRegex = new RegExp(methodTag, 'i');
                            const methodMatch = methodRegex.exec(xmlContent);
                            
                            if (methodMatch) {
                                const methodPos = methodMatch.index;
                                const lines = xmlContent.substr(0, methodPos).split('\n');
                                const line = lines.length - 1;
                                const character = lines[lines.length - 1].length;
                                return [Location.create(xmlUri, {
                                    start: { line, character },
                                    end: { line, character: character + methodName.length }
                                })];
                            }
                        }
                    }
                    return [Location.create(xmlUri, { 
                        start: { line: 0, character: 0 },
                        end: { line: 0, character: 0 }
                    })];
                }
            }
        } else if (doc.languageId === 'xml') {
            // Jump from XML to Java
            const statementId = getStatementIdAtPosition(doc, params.position);
            if (statementId && xmlMappings.has(statementId)) {
                const javaUri = xmlMappings.get(statementId);
                if (javaUri) {
                    // Try to find exact position in Java
                    const javaPath = javaUri.toString().replace('file://', '');
                    if (fs.existsSync(javaPath)) {
                        const javaContent = fs.readFileSync(javaPath, 'utf8');
                        
                        // 改进方法名匹配，支持各种修饰符和注解
                        const methodRegex = new RegExp(
                            `(?:@[A-Za-z]+(?:\\([^)]*\\))?\\s*)*` + // 注解
                            `(?:public\\s+)?(?:abstract\\s+)?` +    // 修饰符
                            `(?:[A-Za-z0-9_<>\\[\\],\\s]+)\\s+` +  // 返回类型
                            `\\b${escapeRegExp(statementId)}\\s*\\(`       // 方法名
                        , 'i');
                        
                        const methodMatch = methodRegex.exec(javaContent);
                        if (methodMatch) {
                            const methodPos = methodMatch.index;
                            const lines = javaContent.substr(0, methodPos).split('\n');
                            const line = lines.length - 1;
                            
                            // 找到方法名在行中的位置
                            const methodLine = lines[lines.length - 1];
                            const character = methodLine.indexOf(statementId);
                            
                            return [Location.create(javaUri, {
                                start: { line, character },
                                end: { line, character: character + statementId.length }
                            })];
                        }
                    }
                    return [Location.create(javaUri, { 
                        start: { line: 0, character: 0 },
                        end: { line: 0, character: 0 }
                    })];
                }
            }
        }
    } catch (error) {
        connection.console.error(`Definition provider error: ${error}`);
    }

    return [];
});

// Make the text document manager listen on the connection
// for open, change and close text document events
documents.listen(connection);

// Listen on the connection
connection.listen();

// Helper functions
function parseJavaMapper(doc: TextDocument) {
    try {
        const text = doc.getText();
        const uri = doc.uri;
        
        // Update cache
        fileCache.set(uri.toString().replace('file://', ''), {
            version: doc.version,
            content: text
        });

        // Check if it's a Mapper interface
        if (!text.includes('@Mapper') && !text.includes('public interface')) {
            return;
        }

        // Find corresponding XML file
        const xmlPath = uri.toString().replace('.java', '.xml').replace('file://', '');
        if (!fs.existsSync(xmlPath)) {
            return;
        }

        // 改进方法定义的正则表达式，支持更多的Java语法
        const methodRegex = /(?:@[A-Za-z]+(?:\([^)]*\))?\s*)*(?:public\s+)?(?:abstract\s+)?(?:[A-Za-z0-9_<>[\],\s]+)\s+([A-Za-z][A-Za-z0-9_]*)\s*\([^)]*\)/g;
        let match;
        while ((match = methodRegex.exec(text)) !== null) {
            const methodName = match[1];
            mapperMappings.set(methodName, uri.replace('.java', '.xml'));
        }
    } catch (error) {
        connection.console.error(`parseJavaMapper error: ${error}`);
    }
}

function parseXmlMapper(doc: TextDocument) {
    try {
        const text = doc.getText();
        const uri = doc.uri;

        // Update cache
        fileCache.set(uri.toString().replace('file://', ''), {
            version: doc.version,
            content: text
        });

        // Parse namespace (Java interface path)
        const namespaceMatch = text.match(/namespace="([^"]+)"/);
        if (!namespaceMatch) return;

        const javaPath = namespaceMatch[1].replace(/\./g, '/') + '.java';
        const javaUri = uri.replace('.xml', '.java');

        // 改进SQL语句ID的正则表达式，支持更多的MyBatis元素和引号样式
        const idRegex = /<(select|insert|update|delete|sql|resultMap)\s+[^>]*id=["']([^"']+)["'][^>]*/g;
        let match;
        while ((match = idRegex.exec(text)) !== null) {
            const statementId = match[2];
            xmlMappings.set(statementId, javaUri);
        }
    } catch (error) {
        connection.console.error(`parseXmlMapper error: ${error}`);
    }
}

function getMethodNameAtPosition(doc: TextDocument, position: any): string | undefined {
    try {
        if (!doc || typeof doc.getText !== 'function') return undefined;
        
        // 如果在注释中，不处理
        if (isInComment(doc, position)) {
            return undefined;
        }
        
        const text = doc.getText();
        const lines = text.split('\n');
        const line = Math.max(0, Math.min(position.line, lines.length - 1));
        const lineText = lines[line];
        
        // 查找方法定义行，使用改进的正则表达式
        const methodRegex = /(?:@[A-Za-z]+(?:\([^)]*\))?\s*)*(?:public\s+)?(?:abstract\s+)?(?:[A-Za-z0-9_<>[\],\s]+)\s+([A-Za-z][A-Za-z0-9_]*)\s*\([^)]*\)/;
        const methodMatch = lineText.match(methodRegex);
        if (methodMatch) {
            return methodMatch[1];
        }

        // 检查光标是否在方法名上
        const pos: Position = { line: position.line, character: position.character };
        const wordRange = getWordRange(doc, pos);
        if (wordRange) {
            const word = doc.getText(wordRange);
            
            // 验证这个单词是否是文件中的方法名
            // 1. 检查标准方法定义
            const methodDefRegex = new RegExp(
                `(?:@[A-Za-z]+(?:\\([^)]*\\))?\\s*)*` + // 注解
                `(?:public\\s+)?(?:abstract\\s+)?` +    // 修饰符
                `(?:[A-Za-z0-9_<>\\[\\],\\s]+)\\s+` +  // 返回类型
                `\\b${escapeRegExp(word)}\\s*\\(`       // 方法名
            , 'gm');
            
            if (methodDefRegex.test(text)) {
                return word;
            }

            // 2. 检查Mapper接口中的方法引用
            const mapperMethodRegex = new RegExp(
                `@(?:Select|Insert|Update|Delete|Options)\\([^)]*\\)\\s*` +
                `(?:[A-Za-z0-9_<>\\[\\],\\s]+)\\s+` +
                `\\b${escapeRegExp(word)}\\s*\\(`
            , 'i');
            
            if (mapperMethodRegex.test(text)) {
                return word;
            }
            
            // 3. 检查方法调用
            const methodCallRegex = new RegExp(`\\.\\s*${escapeRegExp(word)}\\s*\\(`, 'g');
            if (methodCallRegex.test(text)) {
                // 验证这是否是一个在Mapper接口中定义的方法
                // 检查文件是否是Mapper接口
                if (text.includes('@Mapper') || 
                    text.includes('public interface') || 
                    doc.uri.toString().toLowerCase().includes('mapper')) {
                    return word;
                }
            }
        }

        return undefined;
    } catch (error) {
        connection.console.error(`getMethodNameAtPosition error: ${error}`);
        return undefined;
    }
}

function getStatementIdAtPosition(doc: TextDocument, position: any): string | undefined {
    try {
        if (!doc || typeof doc.getText !== 'function') return undefined;
        
        // 如果在XML注释中，不处理
        if (isInXmlComment(doc, position)) {
            return undefined;
        }
        
        const text = doc.getText();
        const lines = text.split('\n');
        const line = Math.max(0, Math.min(position.line, lines.length - 1));
        const lineText = lines[line];
        
        // 查找当前行的语句ID，支持单引号和双引号
        const statementRegex = /<(select|insert|update|delete|sql|resultMap|parameterMap)[^>]*\bid\s*=\s*["']([^"']+)["'][^>]*>/i;
        const statementMatch = lineText.match(statementRegex);
        if (statementMatch) {
            return statementMatch[2];
        }

        // 检查光标位置的单词
        const pos: Position = { line: position.line, character: position.character };
        const wordRange = getWordRange(doc, pos);
        if (wordRange) {
            const word = doc.getText(wordRange);
            
            // 验证这个单词是否是文件中的语句ID
            // 1. 检查所有MyBatis语句类型
            const statements = [
                'select', 'insert', 'update', 'delete',
                'sql', 'resultMap', 'parameterMap',
                'cache', 'cache-ref', 'association',
                'collection', 'discriminator'
            ];
            
            for (const stmt of statements) {
                // 使用更灵活的正则表达式匹配，支持不同的引号样式和空格
                const idDefRegex = new RegExp(
                    `<${stmt}[^>]*\\bid\\s*=\\s*["']${escapeRegExp(word)}["'][^>]*>`,
                    'i'
                );
                if (idDefRegex.test(text)) {
                    return word;
                }
            }

            // 2. 检查refid引用
            const refRegex = new RegExp(
                `\\b(?:refid|resultMap|extends)\\s*=\\s*["']${escapeRegExp(word)}["']`,
                'i'
            );
            if (refRegex.test(text)) {
                // 验证这个引用是否确实存在于当前文件或其他映射文件中
                const idExistsRegex = new RegExp(
                    `<(?:sql|resultMap)[^>]*\\bid\\s*=\\s*["']${escapeRegExp(word)}["'][^>]*>`,
                    'i'
                );
                if (idExistsRegex.test(text)) {
                    return word;
                }
            }

            // 3. 检查include语句
            const includeRegex = new RegExp(
                `<include\\s+refid\\s*=\\s*["']${escapeRegExp(word)}["']\\s*/?>`,
                'i'
            );
            if (includeRegex.test(text)) {
                return word;
            }
        }

        return undefined;
    } catch (error) {
        connection.console.error(`getStatementIdAtPosition error: ${error}`);
        return undefined;
    }
}